package com.telenav.comm;

import java.util.Hashtable;
import java.util.Vector;

import com.telenav.network.NetworkManager;
import com.telenav.threadpool.ThreadPool;

public class Comm implements Runnable {

	public static int REQUEST_JOB_RETRY_WAIT_TIME = 60000;

    protected NetworkManager networkManager;
    
    protected ThreadPool commPool;

    protected long lastNotifyTimestamp = -1;
    
    protected HostProvider hostProvider;
    
    protected Thread timeoutThread;
    
    protected Object timeoutMutex = new Object();
    
    public Comm(NetworkManager networkManager, ThreadPool commPool)
    {
        if (networkManager == null)
        {
            throw new IllegalArgumentException("The networkManager is null.");
        }
        if (commPool == null)
        {
            throw new IllegalArgumentException("The commPool is null.");
        }

        this.networkManager = networkManager;
        this.commPool = commPool;
    }
    
    public HostProvider getHostProvider()
    {
        if(this.hostProvider == null)
        {
            this.hostProvider = new HostProvider();
        }
        
        return this.hostProvider;
    }
    
    public void setHostProvider(HostProvider hostProvider)
    {
        this.hostProvider = hostProvider;
    }
    
    public String sendData(String requestId, Host host, byte[] request, int retryTimes, long timeout, ICommCallback commCallBack)
    {
        return this.sendData(requestId, host, request, retryTimes, timeout, commCallBack, null);
    }
    
    public String sendData(String requestId, Host host, byte[] request, int retryTimes, long timeout, ICommCallback commCallBack, Hashtable requestHeaders)
    {
        if(requestId == null || requestId.length() == 0)
        {
            requestId = "comm_" + System.currentTimeMillis();
        }
        else
        {
            requestId = requestId + "_" + System.currentTimeMillis();
        }
        
        RequestJob job = new RequestJob(requestId, host, request, retryTimes, timeout, commCallBack, this, requestHeaders);

        addJob(job, false);

        return job.requestId;
    }

    public void cancelAll()
    {
        Vector jobs = this.commPool.getCurrentJobs();
        cancelJob(jobs, null);
    }

    public void cancelJob(String requestId)
    {
        Vector jobs = this.commPool.getCurrentJobs();
        if (cancelJob(jobs, requestId))
        {
            return;
        }
    }

    public void addJob(RequestJob job, boolean isInFront)
    {
    	if(isInFront)
        {
            this.commPool.insertJobInFront(job);
        }
        else
        {
            this.commPool.addJob(job);
        }
        
        if(this.timeoutThread == null)
        {
            this.timeoutThread = new Thread(this, "comm-timeout-thread");
            this.timeoutThread.start();
        }
        
        synchronized (timeoutMutex)
        {
            timeoutMutex.notifyAll();
        }
    }
    
    private boolean cancelJob(Vector jobs, String requestId)
    {
        for (int i = 0; i < jobs.size(); i++)
        {
            Object jobObj = jobs.elementAt(i);
            if (jobObj instanceof RequestJob)
            {
                RequestJob job = (RequestJob) jobObj;
                if(requestId == null)
                {
                    job.cancel();
                }
                else if (job.requestId.equals(requestId) || job.requestId.indexOf(requestId) != -1)
                {
                    job.cancel();
                    return true;
                }
            }
        }

        return false;
    }
    
    public void run()
    {
        while (true)
        {
            try
            {
                handleTimeoutJobs(commPool.getCurrentJobs());

                synchronized (timeoutMutex)
                {
                    if (commPool.getCurrentJobs().size() > 0 )
                    {
                        timeoutMutex.wait(1000);
                    }
                    else
                    {
                        timeoutMutex.wait(6000000);
                    }
                }
            }
            catch (Exception e)
            {
            }
        }
    }

    private void handleTimeoutJobs(Vector jobs)
    {
        for (int i = 0; i < jobs.size(); i++)
        {
            Object jobObj = jobs.elementAt(i);
            if(jobObj instanceof RequestJob)
            {
                RequestJob job = (RequestJob)jobObj;
                
                if(job.executeTime <= 0)
                {
                    continue;
                }
                
                long waitTime = (job.executeTime + job.timout) - System.currentTimeMillis();

                if (!job.isCancelled() && waitTime <= 0)
                {
                    job.timeout();
                }
            }
        }
    }

}
